#region File Description
//-----------------------------------------------------------------------------
// Player.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System.Collections.Generic;

namespace HOPE {
	/// <summary>
	/// Our Heroes
	/// </summary>
	class Player {
		// Animations

		private Animation idleAnimation;
		private Animation runAnimation;
		private Animation jumpAnimation;
		private Animation crouchAnimation;
		private Animation dieAnimation;
		private SpriteEffects flip = SpriteEffects.None;
		private AnimationPlayer sprite;
		string baseTextureName;

		//----------------------------------------------------------------------------
		//9. Add Property
		public Rectangle ThumbStickArea { get; set; }
		//----------------------------------------------------------------------------
		// Sounds
		private SoundEffect jumpSound;
		private SoundEffect fallSound;

		Character character;

		public Character CharacterClass {
			get { return character; }
		}

		public LevelEngine Level {
			get { return level; }
		}
		LevelEngine level;

		public bool IsAlive {
			get { return isAlive; }
		}
		bool isAlive;

		// Physics state
		public Vector2 Position {
			get { return position; }
			set { position = value; }
		}
		Vector2 position;

		private float previousBottom;

		public Vector2 Velocity {
			get { return velocity; }
			set { velocity = value; }
		}
		Vector2 velocity;

		// Constants for controling horizontal movement
		private const float MoveAcceleration = 10000.0f;
		private const float MaxMoveSpeed = 1000.0f;
		private const float GroundDragFactor = 0.38f;
		private const float AirDragFactor = 0.48f;

		// Constants for controlling vertical movement
		private const float MaxJumpTime = 0.35f;
		private const float JumpLaunchVelocity = -3500.0f;
		private const float GravityAcceleration = 3400.0f;
		private const float MaxFallSpeed = 550.0f;
		private const float JumpControlPower = 0.14f;

		// Input configuration
		private const float MoveStickScale = 1.0f;
		private const float AccelerometerScale = 1.5f;
		private const Buttons JumpButton = Buttons.A;

		/// <summary>
		/// Gets whether or not the player's feet are on the ground.
		/// </summary>
		public bool IsOnGround {
			get { return isOnGround; }
		}
		bool isOnGround;

		/// <summary>
		/// Current user movement input.
		/// </summary>
		private float movement;

		// Jumping state
		private bool isJumping;
		private bool wasJumping;
		private float jumpTime;

		private Rectangle localBounds;
		//private LevelEngine levelEngine;
		//private Vector2 start;
		/// <summary>
		/// Gets a rectangle which bounds this player in world space.
		/// </summary>
		public Rectangle BoundingRectangle {
			get {
				int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
				int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;

				return new Rectangle(left, top, localBounds.Width, localBounds.Height);
			}
		}

		/// <summary>
		/// Constructors a new player.
		/// </summary>
		//public Player (LevelEngine level, Vector2 position,string Texture,Character CharacterClass) {
		public Player(Character CharacterClass,string Texture,Vector2 position){
			//this.level = level;
			switch (CharacterClass) {
				case Character.Audrianna: baseTextureName = "Audrianna"; break;
				case Character.Davina: baseTextureName = "Davina"; break;
				case Character.Latifu: baseTextureName = "Latifu"; break;
				case Character.Zakhaev: baseTextureName = "Zakhaev"; break;
			}
			this.character = CharacterClass;
			LoadContent();

			Reset(position);
		}

		//public Player (LevelEngine levelEngine, Vector2 start) {pro
		//    // TODO: Complete member initialization
		//    this.levelEngine = levelEngine;
		//    this.start = start;
		//}

		/// <summary>
		/// Loads the player sprite sheet and sounds.
		/// </summary>
		public void LoadContent () {
			// Load animated textures.
#if DEBUG
			System.Diagnostics.Debug.WriteLine(Level.Content.RootDirectory);
#endif
			idleAnimation = new Animation(Level.Content.Load<Texture2D>("Textures/Characters/Sprites/Hero/"+baseTextureName+
				"/"+baseTextureName+"_Walk"), 0.1f, true);
			runAnimation = new Animation(Level.Content.Load<Texture2D>("Textures/Characters/Sprites/Hero/" + baseTextureName +
				"/" + baseTextureName + "_Run"), 1.5f, true);
			jumpAnimation = new Animation(Level.Content.Load<Texture2D>("Textures/Characters/Sprites/Hero/" + baseTextureName +
				"/" + baseTextureName + "_Jump"), 0.1f, false);
			crouchAnimation = new Animation(Level.Content.Load<Texture2D>("Textures/Characters/Sprites/Hero/" + baseTextureName +
				"/" + baseTextureName + "_Crouch"), 0.1f, false);
			//dieAnimation = new Animation(Leve5l.Content.Load<Texture2D>("Sprites/Player/Die"), 0.1f, false);
			// Calculate bounds within texture size.            
			int width = (int)(idleAnimation.FrameWidth - 10);
			int left = 7;
			int height = (int)(idleAnimation.FrameHeight);
			int top = idleAnimation.FrameHeight - height;
			localBounds = new Rectangle(left, top, width, height);

			//--------------------------------------------------------------------------------
			//10. Initialize ThumbStickArea
			ThumbStickArea = new Rectangle((int)HOPEGame.controlstickBoundaryPosition.X,
										   (int)HOPEGame.controlstickBoundaryPosition.Y,
										   HOPEGame.controlstickBoundary.Width,
										   HOPEGame.controlstickBoundary.Height);
		}

		/// <summary>
		/// Resets the player to life.
		/// </summary>
		/// <param name="position">The position to come to life at.</param>
		public void Reset (Vector2 position) {
			Position = position;
			Velocity = Vector2.Zero;
			isAlive = true;
			sprite.PlayAnimation(idleAnimation);
		}

		/// <summary>
		/// Handles input, performs physics, and animates the player sprite.
		/// </summary>
		/// <remarks>
		/// We pass in all of the input states so that our game is only polling the hardware
		/// once per frame. We also pass the game's orientation because when using the accelerometer,
		/// we need to reverse our motion when the orientation is in the LandscapeRight orientation.
		/// </remarks>
		public void Update (
			GameTime gameTime,
			KeyboardState keyboardState,
			GamePadState gamePadState,
			TouchCollection touchState,
			//AccelerometerState accelState,
			DisplayOrientation orientation) {
			//If you ever need to go back to old control scheme uncomment this version of GetInput
			//GetInput(keyboardState, gamePadState, touchState, accelState, orientation);

			//11a. Call our newly devised GetInput()
			GetInput();

			ApplyPhysics(gameTime);

			if (IsAlive && IsOnGround) {
				if (Math.Abs(Velocity.X) - 0.02f > 0) {
					sprite.PlayAnimation(runAnimation);
				}
				else {
					sprite.PlayAnimation(idleAnimation);
				}
			}

			// Clear input.
			movement = 0.0f;
			isJumping = false;
		}

		//11. Gets Player horizontal movement and jump commands from input.
		private void GetInput () {
			//Left ThumbStick to move player
			if (VirtualThumbsticks.LeftThumbstickCenter.HasValue) {
				Rectangle touchPointRectangle = new Rectangle((int)VirtualThumbsticks.LeftThumbstickCenter.Value.X,
				(int)VirtualThumbsticks.LeftThumbstickCenter.Value.Y, 1, 1);

				// If the touch is in the stick
				if (ThumbStickArea.Contains((int)VirtualThumbsticks.LeftThumbstickCenter.Value.X,
											(int)VirtualThumbsticks.LeftThumbstickCenter.Value.Y)) {
					//Get analog horizontal movement
					movement = VirtualThumbsticks.LeftThumbstick.X * MoveStickScale;
				}
			}

			if (VirtualThumbsticks.RightThumbstickCenter.HasValue) {
				Rectangle buttonRectangle = new Rectangle((int)HOPEGame.jumpButtonPosition.X, (int)HOPEGame.jumpButtonPosition.Y,
																	HOPEGame.jumpButton.Width / 2, HOPEGame.jumpButton.Height);
				// Touch Bounds
				Rectangle touchRectangle = new Rectangle((int)VirtualThumbsticks.RightThumbstickCenter.Value.X,
														(int)VirtualThumbsticks.RightThumbstickCenter.Value.Y, 1, 1);
				// If the touch is in the button
				if (buttonRectangle.Contains(touchRectangle)) {
					HOPEGame.isJumpButtonClicked = true;
				}
			}
			//Check if player wants to Jump
			isJumping = HOPEGame.isJumpButtonClicked;
		}

		//#region Old_GetInput
		///// <summary>
		///// Gets player horizontal movement and jump commands from input.
		///// </summary>
		//private void GetInput (
		//    KeyboardState keyboardState,
		//    GamePadState gamePadState,
		//    TouchCollection touchState,
		//    AccelerometerState accelState,
		//    DisplayOrientation orientation) {
		//    // Get analog horizontal movement.
		//    movement = gamePadState.ThumbSticks.Left.X * MoveStickScale;

		//    // Ignore small movements to prevent running in place.
		//    if (Math.Abs(movement) < 0.5f)
		//        movement = 0.0f;

		//    // Move the player with accelerometer
		//    if (Math.Abs(accelState.Acceleration.Y) > 0.10f) {
		//        // set our movement speed
		//        movement = MathHelper.Clamp(-accelState.Acceleration.Y * AccelerometerScale, -1f, 1f);

		//        // if we're in the LandscapeLeft orientation, we must reverse our movement
		//        if (orientation == DisplayOrientation.LandscapeRight)
		//            movement = -movement;
		//    }

		//    // If any digital horizontal movement input is found, override the analog movement.
		//    if (gamePadState.IsButtonDown(Buttons.DPadLeft) ||
		//        keyboardState.IsKeyDown(Keys.Left) ||
		//        keyboardState.IsKeyDown(Keys.A)) {
		//        movement = -1.0f;
		//    }
		//    else if (gamePadState.IsButtonDown(Buttons.DPadRight) ||
		//             keyboardState.IsKeyDown(Keys.Right) ||
		//             keyboardState.IsKeyDown(Keys.D)) {
		//        movement = 1.0f;
		//    }

		//    // Check if the player wants to jump.
		//    isJumping =
		//        gamePadState.IsButtonDown(JumpButton) ||
		//        keyboardState.IsKeyDown(Keys.Space) ||
		//        keyboardState.IsKeyDown(Keys.Up) ||
		//        keyboardState.IsKeyDown(Keys.W) ||
		//        touchState.AnyTouch();
		//}
		//#endregion

		/// <summary>
		/// Updates the player's velocity and position based on input, gravity, etc.
		/// </summary>
		public void ApplyPhysics (GameTime gameTime) {
			float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

			Vector2 previousPosition = Position;

			// Base velocity is a combination of horizontal movement control and
			// acceleration downward due to gravity.
			velocity.X += movement * MoveAcceleration * elapsed;
			velocity.Y = MathHelper.Clamp(velocity.Y + GravityAcceleration * elapsed, -MaxFallSpeed, MaxFallSpeed);

			velocity.Y = DoJump(velocity.Y, gameTime);

			// Apply pseudo-drag horizontally.
			if (IsOnGround)
				velocity.X *= GroundDragFactor;
			else
				velocity.X *= AirDragFactor;

			// Prevent the player from running faster than his top speed.            
			velocity.X = MathHelper.Clamp(velocity.X, -MaxMoveSpeed, MaxMoveSpeed);

			// Apply velocity.
			Position += velocity * elapsed;
			Position = new Vector2((float)Math.Round(Position.X), (float)Math.Round(Position.Y));

			// If the player is now colliding with the level, separate them.
			//HandleCollisions();

			// If the collision stopped us from moving, reset the velocity to zero.
			if (Position.X == previousPosition.X)
				velocity.X = 0;

			if (Position.Y == previousPosition.Y)
				velocity.Y = 0;
		}

		/// <summary>
		/// Calculates the Y velocity accounting for jumping and
		/// animates accordingly.
		/// </summary>
		/// <remarks>
		/// During the accent of a jump, the Y velocity is completely
		/// overridden by a power curve. During the decent, gravity takes
		/// over. The jump velocity is controlled by the jumpTime field
		/// which measures time into the accent of the current jump.
		/// </remarks>
		/// <param name="velocityY">
		/// The player's current velocity along the Y axis.
		/// </param>
		/// <returns>
		/// A new Y velocity if beginning or continuing a jump.
		/// Otherwise, the existing Y velocity.
		/// </returns>
		private float DoJump (float velocityY, GameTime gameTime) {
			// If the player wants to jump
			if (isJumping) {
				// Begin or continue a jump
				if ((!wasJumping && IsOnGround) || jumpTime > 0.0f) {

					jumpTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
					sprite.PlayAnimation(jumpAnimation);
				}

				// If we are in the ascent of the jump
				if (0.0f < jumpTime && jumpTime <= MaxJumpTime) {
					// Fully override the vertical velocity with a power curve that gives players more control over the top of the jump
					velocityY = JumpLaunchVelocity * (1.0f - (float)Math.Pow(jumpTime / MaxJumpTime, JumpControlPower));
				}
				else {
					// Reached the apex of the jump
					jumpTime = 0.0f;
				}
			}
			else {
				// Continues not jumping or cancels a jump in progress
				jumpTime = 0.0f;
			}
			wasJumping = isJumping;

			return velocityY;
		}

		/// <summary>
		/// Detects and resolves all collisions between the player and his neighboring
		/// tiles. When a collision is detected, the player is pushed away along one
		/// axis to prevent overlapping. There is some special logic for the Y axis to
		/// handle platforms which behave differently depending on direction of movement.
		/// </summary>
		//private void HandleCollisions () {
		//    // Get the player's bounding rectangle and find neighboring tiles.
		//    Rectangle bounds = BoundingRectangle;
		//    int leftTile = (int)Math.Floor((float)bounds.Left / Tile.Width);
		//    int rightTile = (int)Math.Ceiling(((float)bounds.Right / Tile.Width)) - 1;
		//    int topTile = (int)Math.Floor((float)bounds.Top / Tile.Height);
		//    int bottomTile = (int)Math.Ceiling(((float)bounds.Bottom / Tile.Height)) - 1;

		//    // Reset flag to search for ground collision.
		//    isOnGround = false;

		//    // For each potentially colliding tile,
		//    for (int y = topTile; y <= bottomTile; ++y) {
		//        for (int x = leftTile; x <= rightTile; ++x) {
		//            // If this tile is collidable,
		//            //TileCollision collision
		//            if (collision != TileCollision.Passable) {
		//                // Determine collision depth (with direction) and magnitude.
		//                Rectangle tileBounds;
		//                Vector2 depth = RectangleExtensions.GetIntersectionDepth(bounds, tileBounds);
		//                if (depth != Vector2.Zero) {
		//                    float absDepthX = Math.Abs(depth.X);
		//                    float absDepthY = Math.Abs(depth.Y);

		//                    // Resolve the collision along the shallow axis.
		//                    if (absDepthY < absDepthX || collision == TileCollision.Platform) {
		//                        // If we crossed the top of a tile, we are on the ground.
		//                        if (previousBottom <= tileBounds.Top)
		//                            isOnGround = true;

		//                        // Ignore platforms, unless we are on the ground.
		//                        if (collision == TileCollision.Impassable || IsOnGround) {
		//                            // Resolve the collision along the Y axis.
		//                            Position = new Vector2(Position.X, Position.Y + depth.Y);

		//                            // Perform further collisions with the new bounds.
		//                            bounds = BoundingRectangle;
		//                        }
		//                    }
		//                    else if (collision == TileCollision.Impassable) // Ignore platforms.
		//                    {
		//                        // Resolve the collision along the X axis.
		//                        Position = new Vector2(Position.X + depth.X, Position.Y);

		//                        // Perform further collisions with the new bounds.
		//                        bounds = BoundingRectangle;
		//                    }
		//                }
		//            }
		//        }
		//    }

		//    // Save the new bounds bottom.
		//    previousBottom = bounds.Bottom;
		//}

		///// <summary>
		///// Called when the player has been killed.
		///// </summary>
		///// <param name="killedBy">
		///// The enemy who killed the player. This parameter is null if the player was
		///// not killed by an enemy (fell into a hole).
		///// </param>
		//public void OnKilled (Enemy killedBy) {
		//    isAlive = false;

		//    if (killedBy != null)
		//        killedSound.Play();
		//    else
		//        fallSound.Play();

		//    sprite.PlayAnimation(dieAnimation);
		//}

		/// <summary>
		/// Called when this player reaches the level's exit.
		/// </summary>
		public void OnReachedExit () {
			sprite.PlayAnimation(crouchAnimation);
		}

		/// <summary>
		/// Draws the animated player.
		/// </summary>
		public void Draw (GameTime gameTime, SpriteBatch spriteBatch) {
			// Flip the sprite to face the way we are moving.
			if (Velocity.X < 0)
				flip = SpriteEffects.FlipHorizontally;
			else if (Velocity.X > 0)
				flip = SpriteEffects.None;

			// Draw that sprite.
			sprite.Draw(gameTime, spriteBatch, Position, flip);
		}
	}
	enum Character {
		Audrianna,
		Zakhaev,
		Latifu,
		Davina,
	}
}
